package org.excilys.training.ebanking.service;

import java.util.ArrayList;
import java.util.List;

import org.excilys.training.ebanking.idao.IDaoOperation;
import org.excilys.training.ebanking.iservice.IServiceOperation;
import org.excilys.training.ebanking.model.Operation;
import org.joda.time.DateTime;
import org.joda.time.LocalDateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.googlecode.ehcache.annotations.Cacheable;

@Service
@Transactional(readOnly = true)
public class ServiceOperation implements IServiceOperation {

	private static final Logger LOGGER = LoggerFactory
			.getLogger(ServiceOperation.class);
	@Autowired
	private IDaoOperation daoOperation;
	@SuppressWarnings("unused")
	private LocalDateTime todayDate;
	@Value("${tailleListeOperations}")
	private int tailleListeOperations;

	public ServiceOperation() {
		this.todayDate = new DateTime().toLocalDateTime();
	}

	public IDaoOperation getDaoOperation() {
		return daoOperation;
	}

	public void setDaoOperation(IDaoOperation daoOperation) {
		this.daoOperation = daoOperation;
	}

	public static double round(double Rval, int Rpl) {
		double p = (double) Math.pow(10, Rpl);
		Rval = Rval * p;
		double tmp = Math.round(Rval);
		return (double) tmp / p;
	}

	/**
	 * Méthode cherhant à obtenir la liste des opérations qui n'ont pas été
	 * effectué par carte pour un compte et un mois donnée
	 * <p>
	 *
	 * @param id
	 *            numéro de compte
	 * @param mois
	 *            mois recherché
	 * @param year
	 *            année recherchée
	 *
	 * @return liste des opérations du mois
	 */
	public List<Operation> getAllNoCarteOperationsByMonth(int id, int mois,
			int year) {

		List<Operation> operations = daoOperation
				.getAllNoCarteOperationsByCompte(id);

		List<Operation> operationsByMonth = new ArrayList<Operation>();
		int currentMonth = new DateTime().withMonthOfYear(mois)
				.getMonthOfYear();
		DateTime date = null;

		if (!operations.isEmpty()) {
			LOGGER.info("Chargement des operations sans carte  pour le mois /"
					+ mois + "/" + year + " et pour le compte n°{}", id);
			for (Operation o : operations) {

				date = o.getDateOperation();

				if (date.getYear() == year
						&& date.getMonthOfYear() == currentMonth) {
					operationsByMonth.add(o);
				}
			}
		}

		return operationsByMonth;

	}

	/**
	 * Méthode permettant de récupérer la liste des opérations par carte pour un
	 * compte et un mois donné.
	 * <p>
	 *
	 * @param id
	 *            numéro du compte
	 * @param mois
	 *            mois de l'année choisie
	 * @param annee
	 *            choisie
	 *
	 * @return liste des operations nécessitant une carte
	 */
	public List<Operation> getAllCarteOperationByMonth(int id, int mois,
			int year) {

		List<Operation> operations = daoOperation
				.getAllCarteOperationsByCompte(id);

		List<Operation> operationsByMonth = new ArrayList<Operation>();
		int currentMonth = new DateTime().withMonthOfYear(mois)
				.getMonthOfYear();
		DateTime date = null;

		if (!operations.isEmpty()) {
			LOGGER.info("Chargement des operations sans carte  pour le mois /"
					+ mois + "/" + year + " et pour le compte n°{}", id);

			for (Operation o : operations) {
				date = o.getDateOperation();

				if (date.getYear() == year
						&& date.getMonthOfYear() == currentMonth) {
					operationsByMonth.add(o);
				}
			}
		}
		return operationsByMonth;
	}

	/**
	 * Méthode permettant d'obtenir la somme des montants des opérations
	 * effectués par carte pour un compte et un mois donnée
	 * <p>
	 *
	 * @param id
	 *            numéro de compte
	 * @param mois
	 *            mois de l'anneé courante
	 *
	 * @return total des montants
	 */
	public double getMontantOperationsCarteByMonth(int id, int mois, int year) {

		double montantTotal = 0;
		List<Double> operationsCarte = daoOperation
				.getMontantsOperationsCarteByMonth(id, mois, year);

		if (!operationsCarte.isEmpty()) {
			for (Double montant : operationsCarte) {
				montantTotal += montant;
			}

			montantTotal = round(montantTotal, 2);
			LOGGER.info(
					"Chargement du montant des operations cartes du mois /{}/"
							+ year + " du compte n°{}", mois, id);
			return montantTotal;
		}
		LOGGER.warn("On attribut 0.0 au montant des operations par carte du mois:/"
				+ mois + "/" + year + " pour le mois");
		return 0.0;
	}

	@Cacheable(cacheName = "operationsCarteCache")
	public List<Operation> getSomeCardOperations(int id, int mois, int year,
			int firstoperation) {

		LOGGER.info(
				"Chargement d'une tranche  d'opérations   carte pour le mois :/{}/{} du compte n°'"
						+ id + "'", mois, year);

		List<Operation> operations = daoOperation
				.getSomeCarteOperationsByCompte(id, mois, year, firstoperation,
						tailleListeOperations);

		LOGGER.debug("-------taille  liste d'operations carte= {} -------",
				operations.size());

		if (operations.isEmpty()) {
			LOGGER.warn(
					"la tranche de d'opérations par carte est vide pour le mois :/{}/{} du compte n°'"
							+ id + "'", mois, year);
		}

		return operations;
	}

	@Cacheable(cacheName = "operationsSansCarteCache")
	// FIXME cache
	public List<Operation> getSomeNoCardOperations(int id, int mois, int year,
			int firstoperation) {

		LOGGER.info(
				"Chargement d'une tranche  d'opérations sans carte pour le mois :/{}/{} du compte n°'"
						+ id + "'", mois, year);
		List<Operation> operations = daoOperation
				.getSomeNoCarteOperationsByCompte(id, mois, year,
						firstoperation, tailleListeOperations);
		LOGGER.debug(
				"-------taille de liste d operatio sans carte= {} -------",
				operations.size());
		if (operations.isEmpty()) {
			LOGGER.warn(
					"la tranche de d'opérations sans carte est vide pour le mois :/{}/{} du compte n°'"
							+ id + "'", mois, year);
		}

		return operations;
	}

	@Cacheable(cacheName = "nombreOperationsCarteCache")
	// FIXME cache
	public int getNombreOperationsCard(int id, int mois, int year) {

		int nombreOperationCard = daoOperation.getNombreOperationsCard(id,
				mois, year);

		if (nombreOperationCard != 0) {
			LOGGER.info(
					"Chargement du nombre d'opérations avec carte pour le mois :/{}/{} du compte n°'"
							+ id + "'", mois, year);
		}
		return nombreOperationCard;
	}

	@Cacheable(cacheName = "nomnbeOperationsSansCarteCache")
	public int getNombreOperationsWithoutCard(int id, int mois, int year) {

		int nombreOperationWithoutCard = daoOperation
				.getNombreOperationsWithoutCard(id, mois, year);

		if (nombreOperationWithoutCard != 0) {
			LOGGER.info(
					"Chargement du nombre d'opérations sans carte pour le mois :/{}/{} du compte n°'"
							+ id + "'", mois, year);
		}
		return nombreOperationWithoutCard;

	}

	public int getNombreOperationsByList() {
		return tailleListeOperations;
	}

	public List<Operation> getAllVirementByAccount(int idCompte) {

		List<Operation> listVirement = daoOperation.getAllVirement(idCompte);

		if (!listVirement.isEmpty()) {
			LOGGER.info(
					"Chargement de la liste d'operations du a un virement pour le compte n°{}",
					idCompte);
		}
		return listVirement;
	}

}
